/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package macro.ide;

import java.awt.Color;
import java.awt.event.InputMethodListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.Utilities;

/**
 *
 * @author Francisca Tenório
 */
public class Main extends javax.swing.JFrame {
    /*
     * Globals var
     */

    String pathMacroFile;
    Integer currentNumberRow, currentColumnRow, positionOpenScope;
    Integer positionCloseScope = 0;
    boolean scopeMarked = false;
    private JMenuItem autoCompleteItem;

    /**
     * Creates new form Main
     */
    public Main() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        autoComplete = new javax.swing.JPopupMenu();
        jToolBar1 = new javax.swing.JToolBar();
        labelAddressFile = new javax.swing.JLabel();
        jSeparator1 = new javax.swing.JToolBar.Separator();
        labelCaretPosition = new javax.swing.JLabel();
        jSeparator2 = new javax.swing.JToolBar.Separator();
        labelSizeMacro = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        macroCode = new javax.swing.JTextPane();
        listTabbed = new javax.swing.JTabbedPane();
        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        listMacro = new javax.swing.JList();
        jPanel2 = new javax.swing.JPanel();
        jScrollPane3 = new javax.swing.JScrollPane();
        listAutomacro = new javax.swing.JList();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane4 = new javax.swing.JScrollPane();
        listVariable = new javax.swing.JList();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        itemOpenMacro = new javax.swing.JMenuItem();
        itemSaveMacro = new javax.swing.JMenuItem();
        itemSaveMacroAs = new javax.swing.JMenuItem();
        itemRunOpenKore = new javax.swing.JMenuItem();
        jMenu2 = new javax.swing.JMenu();
        itemListDefinitions = new javax.swing.JMenuItem();
        jMenu3 = new javax.swing.JMenu();
        itemFormat = new javax.swing.JMenuItem();
        itemGoToLine = new javax.swing.JMenuItem();
        jMenu4 = new javax.swing.JMenu();
        jMenu5 = new javax.swing.JMenu();
        itemOpenPageInternationalPlugin = new javax.swing.JMenuItem();
        itemOpenPageBrazilianPlugin = new javax.swing.JMenuItem();
        jMenu6 = new javax.swing.JMenu();
        itemOpenPageInternationalIDE = new javax.swing.JMenuItem();
        itemOpenPageBrazilianIDE = new javax.swing.JMenuItem();

        autoComplete.setLabel("");

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jToolBar1.setFloatable(false);
        jToolBar1.setRollover(true);

        labelAddressFile.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N
        labelAddressFile.setText("None open file");
        jToolBar1.add(labelAddressFile);

        jSeparator1.setAutoscrolls(true);
        jToolBar1.add(jSeparator1);

        labelCaretPosition.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N
        labelCaretPosition.setText("Ln: 0  Col: 0");
        jToolBar1.add(labelCaretPosition);
        jToolBar1.add(jSeparator2);

        labelSizeMacro.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N
        labelSizeMacro.setText("Rows: 0");
        labelSizeMacro.setToolTipText("");
        jToolBar1.add(labelSizeMacro);

        macroCode.setFont(new java.awt.Font("Consolas", 0, 12)); // NOI18N
        macroCode.setText("macro kepler {\n\tdo c Kepler é gostosão!!\n\tlog só verdades\n\tvar kepler = 1\n}");
        macroCode.setComponentPopupMenu(autoComplete);
        macroCode.setInheritsPopupMenu(true);
        macroCode.addCaretListener(new javax.swing.event.CaretListener() {
            public void caretUpdate(javax.swing.event.CaretEvent evt) {
                macroCodeCaretUpdate(evt);
            }
        });
        macroCode.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyTyped(java.awt.event.KeyEvent evt) {
                macroCodeKeyTyped(evt);
            }
        });
        jScrollPane2.setViewportView(macroCode);

        listTabbed.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N

        listMacro.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N
        listMacro.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                listMacroMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(listMacro);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 181, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 227, Short.MAX_VALUE)
        );

        listTabbed.addTab("Macro", jPanel1);

        listAutomacro.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N
        listAutomacro.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                listAutomacroMouseClicked(evt);
            }
        });
        jScrollPane3.setViewportView(listAutomacro);

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 181, Short.MAX_VALUE)
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 227, Short.MAX_VALUE)
        );

        listTabbed.addTab("Automacro", jPanel2);

        listVariable.setFont(new java.awt.Font("Consolas", 0, 11)); // NOI18N
        listVariable.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                listVariableMouseClicked(evt);
            }
        });
        jScrollPane4.setViewportView(listVariable);

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 181, Short.MAX_VALUE)
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 227, Short.MAX_VALUE)
        );

        listTabbed.addTab("Variable", jPanel3);

        jMenu1.setText("File");

        itemOpenMacro.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        itemOpenMacro.setText("Open macro");
        itemOpenMacro.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemOpenMacroActionPerformed(evt);
            }
        });
        jMenu1.add(itemOpenMacro);

        itemSaveMacro.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        itemSaveMacro.setText("Save macro");
        itemSaveMacro.setEnabled(false);
        itemSaveMacro.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemSaveMacroActionPerformed(evt);
            }
        });
        jMenu1.add(itemSaveMacro);

        itemSaveMacroAs.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.CTRL_MASK));
        itemSaveMacroAs.setText("Save macro as...");
        itemSaveMacroAs.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemSaveMacroAsActionPerformed(evt);
            }
        });
        jMenu1.add(itemSaveMacroAs);

        itemRunOpenKore.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F9, 0));
        itemRunOpenKore.setText("Run OpenKore");
        itemRunOpenKore.setToolTipText("");
        itemRunOpenKore.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemRunOpenKoreActionPerformed(evt);
            }
        });
        jMenu1.add(itemRunOpenKore);

        jMenuBar1.add(jMenu1);

        jMenu2.setText("Window");

        itemListDefinitions.setText("Visible List of Definitions");
        itemListDefinitions.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemListDefinitionsActionPerformed(evt);
            }
        });
        jMenu2.add(itemListDefinitions);

        jMenuBar1.add(jMenu2);

        jMenu3.setText("Code");

        itemFormat.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.SHIFT_MASK));
        itemFormat.setText("Format");
        itemFormat.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemFormatActionPerformed(evt);
            }
        });
        jMenu3.add(itemFormat);

        itemGoToLine.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_G, java.awt.event.InputEvent.CTRL_MASK));
        itemGoToLine.setText("Go to line...");
        itemGoToLine.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemGoToLineActionPerformed(evt);
            }
        });
        jMenu3.add(itemGoToLine);

        jMenuBar1.add(jMenu3);

        jMenu4.setText("Help");

        jMenu5.setText("Article about the Macro plugin");
        jMenu5.setToolTipText("");

        itemOpenPageInternationalPlugin.setText("International wiki");
        itemOpenPageInternationalPlugin.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemOpenPageInternationalPluginActionPerformed(evt);
            }
        });
        jMenu5.add(itemOpenPageInternationalPlugin);

        itemOpenPageBrazilianPlugin.setText("Brazilian wiki");
        itemOpenPageBrazilianPlugin.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemOpenPageBrazilianPluginActionPerformed(evt);
            }
        });
        jMenu5.add(itemOpenPageBrazilianPlugin);

        jMenu4.add(jMenu5);

        jMenu6.setText("Article about the Macro IDE");

        itemOpenPageInternationalIDE.setText("International wiki");
        itemOpenPageInternationalIDE.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemOpenPageInternationalIDEActionPerformed(evt);
            }
        });
        jMenu6.add(itemOpenPageInternationalIDE);

        itemOpenPageBrazilianIDE.setText("Brazilian wiki");
        itemOpenPageBrazilianIDE.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                itemOpenPageBrazilianIDEActionPerformed(evt);
            }
        });
        jMenu6.add(itemOpenPageBrazilianIDE);

        jMenu4.add(jMenu6);

        jMenuBar1.add(jMenu4);

        setJMenuBar(jMenuBar1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 585, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(listTabbed, javax.swing.GroupLayout.PREFERRED_SIZE, 186, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2)
                    .addComponent(listTabbed))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    public static InputMethodListener autoComplete(String text, JTextComponent editor) {
        editor.setText(editor.getText() + ">>" + text);
        return null;
    }
    
    //
    public static int getRow(int pos, JTextComponent editor) {
        int rn = (pos == 0) ? 1 : 0;
        try {
            int offs = pos;
            while (offs > 0) {
                offs = Utilities.getRowStart(editor, offs) - 1;
                rn++;
            }
        } catch (BadLocationException e) {
        }
        return rn;
    }

    public static int getColumn(int pos, JTextComponent editor) {
        try {
            return pos - Utilities.getRowStart(editor, pos) + 1;
        } catch (BadLocationException e) {
        }
        return -1;
    }

    //
    public static void moveCaret(Integer line, JTextComponent editor) {
        Integer i = 0;
        Integer character = 0;
        String[] listRows;
        listRows = editor.getText().split("\n");

        while (line > i) {
            character += listRows[i].length();
            i++;
        }

        editor.setCaretPosition(character + i - 1);
    }
    
    private void itemOpenMacroActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemOpenMacroActionPerformed
        File file; // Cria uma variável file, do tipo File, e deixa ela como null (vazio/sem nada). Ela vai "segurar" o arquivo escolhido

        JFileChooser fileChooser = new JFileChooser(); // Cria janela de seleção de arquivos
        int returnVal = fileChooser.showOpenDialog(this); // retorna um número (não sei qual) caso uma seleção tenha sido feita

        if (returnVal == JFileChooser.APPROVE_OPTION) { // Compara o número que eu falei acima com o APPROVE_OPTION
            file = fileChooser.getSelectedFile(); // Coloca em file o arquivo escolhido
        } else {
            return;
        }

        String path = file.toPath().toString(); // Pega o caminho do arquivo e converte para String
        labelAddressFile.setText(path); // Escrever o caminho
        pathMacroFile = path;

        try { // Try-catch básico
            macroCode.setText("");
            FileReader fr = new FileReader(file); // Cria uma variável fr, do tipo FileReader, que vai ser o "letior" do arquivo escolhido
            BufferedReader reader = new BufferedReader(fr); // Não sei ao certo como funciona, mas seria um "Buffer" para armazenar os dados

            String line = reader.readLine(); // String line recebe uma linha do reader

            while (line != null) { // Verifica se acabou o texto
                macroCode.setText(macroCode.getText() + line + "\n");
                line = reader.readLine(); // Atualiza a line
            }

            macroCode.setCaretPosition(0);
            itemSaveMacro.setEnabled(true);
        } catch (IOException ioe) { // Apenas para tratar alguma excessão, caso ocorra
            System.err.println(ioe); // Faz um print do erro
            System.exit(1); // Sai do programa, com estado 1 (erro), oposto de estado 0, quando tudo correu bem
        }
    }//GEN-LAST:event_itemOpenMacroActionPerformed

    private void itemSaveMacroActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemSaveMacroActionPerformed
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(pathMacroFile))) {
            macroCode.write(writer);
        } catch (IOException ioe) {
            System.err.println(ioe);
            System.exit(1);
        }
    }//GEN-LAST:event_itemSaveMacroActionPerformed

    private void itemSaveMacroAsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemSaveMacroAsActionPerformed
        File file;

        JFileChooser fileChooser = new JFileChooser();
        int returnVal = fileChooser.showSaveDialog(this);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            file = fileChooser.getSelectedFile();
        } else {
            return;
        }

        String path = file.toPath().toString();
        labelAddressFile.setText(path);

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) {
            macroCode.write(writer);
        } catch (IOException ioe) {
            System.err.println(ioe);
            System.exit(1);
        }
    }//GEN-LAST:event_itemSaveMacroAsActionPerformed

    private void itemFormatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemFormatActionPerformed
        Integer i = -1;
        Integer countIndentation = 0;
        String[] listRows;
        listRows = macroCode.getText().split("\n");
        macroCode.setText("");

        for (String currentRow : listRows) {
            // Find open or close block
            i++;
            currentRow = currentRow.replaceAll("^\\t+", "");


            boolean startBlock = false;
            if (i > 0) {
                listRows[i - 1] = listRows[i - 1].replaceAll("^\\t+", "");
                startBlock = listRows[i - 1].matches("(?:^if.*\\{$|^}\\s*else\\s*\\{$|^}\\s*elsif.*\\{$|^macro.*\\{|^automacro.*\\{|^call\\s*\\{$|^\\[)");
            }

            if (startBlock == true) {
                countIndentation++;
            } else {
                boolean closedBlock = currentRow.matches("(?:^}\\s*else\\s*\\{$|^}\\s*elsif.*\\{$|^}|^])");

                if (closedBlock == true) {
                    countIndentation--;
                }
            }

            // Create spacings
            // TODO: http://stackoverflow.com/questions/2255500/can-i-multiply-strings-in-java-to-repeat-sequences
            //   currentRow = StringUtils.repeat("  ", indentation) + currentRow;

            Integer indentatinLoop = 0;
            String tab = "";
            while (indentatinLoop < countIndentation) {
                indentatinLoop++;
                tab = tab + "	";
            }

            // Replace old code with the new code formatted
            macroCode.setText(macroCode.getText() + tab + currentRow + "\n");
        }
    }//GEN-LAST:event_itemFormatActionPerformed

    private void macroCodeKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_macroCodeKeyTyped
        String[] listRows = macroCode.getText().split("\n");

        // Automatic indentation
        if (evt.getKeyChar() == '\n') {
            String rowPrevious;
            try {
                rowPrevious = listRows[currentNumberRow - 2];
            } catch (Exception ex) {
                return;
            }

            // Count indentation
            Integer countIndentation = 0;

            Pattern p = Pattern.compile("\t");
            Matcher m = p.matcher(rowPrevious);

            while (m.find()) {
                countIndentation++;
            }

            rowPrevious = rowPrevious.replaceAll("^\\t+", "");
            boolean startBlock = rowPrevious.matches("(?:^if.*(?:\\{$|#.*)|^}\\s*else\\s*(?:\\{$|#.*)|^}\\s*elsif.*(?:\\{$|#.*)|^macro.*(?:\\{$|#.*)|^automacro.*(?:\\{$|#.*)|^call\\s*(?:\\{$|#.*))");
            boolean startBlockBracket = rowPrevious.contentEquals("[");

            if (startBlock == true || startBlockBracket == true) {
                countIndentation++;
            } else {
                /* TODO: That part really necessary?
                boolean closedBlock = rowPrevious.matches("(?:^}\\s*else\\s*\\{$|^}\\s*elsif.*\\{$)");

                if (closedBlock == true) {
                    countIndentation--;
                }*/
            }

            if (countIndentation > 0) {
                // Create spacings
                // TODO: http://stackoverflow.com/questions/2255500/can-i-multiply-strings-in-java-to-repeat-sequences
                //   currentRow = StringUtils.repeat("  ", indentation) + currentRow;
                Integer indentatinLoop = 0;
                String tab = "";
                while (indentatinLoop++ < countIndentation) {
                    tab = tab + "	";
                }

                // Add closes scope
                String closesScope = "";
                if (startBlock || startBlockBracket) {
                    // Check if really need add closes scope
                    Integer line = currentNumberRow - 1;
                    boolean findBeginMacro = true;

                    // Find begin of the macro
                    while (findBeginMacro && line > 0) {
                        line--;
                        String currentLine = listRows[line].replaceAll("^\\t+", "");
                        if (currentLine.matches("(?:^macro.*(?:\\{$|#.*)|^call.*(?:\\{$|#.*))")) {
                            findBeginMacro = false;
                        }
                    }

                    if (startBlock) {
                        // Parser the macro has been seeing someone without closes scope
                        Integer countOpenBlock = 0;
                        while (countOpenBlock >= 0 && listRows.length > line + 1) {
                            line++;
                            String currentLine = listRows[line].replaceAll("^\\t+", "");

                            if (currentLine.matches("(?:^macro.*(?:\\{$|#.*)|^automacro.*(?:\\{$|#.*)|^call.*(?:\\{$|#.*))")) {
                                break;
                            }

                            if (currentLine.matches("(?:^if.*(?:\\{$|#.*))")) {
                                countOpenBlock++;
                            } else if (currentLine.matches("(?:^})")) {
                                countOpenBlock--;
                            }
                        }

                        // Create closes scope, if need
                        if (countOpenBlock >= 0) {
                            closesScope = "\n" + tab.substring(1) + "}";
                        }
                    } else if (startBlockBracket) {
                        // Parser the macro has been seeing someone without closes scope
                        Integer countOpenBlock = 0;
                        while (countOpenBlock >= 0 && listRows.length > line + 1) {
                            line++;
                            String currentLine = listRows[line].replaceAll("^\\t+", "");

                            if (currentLine.matches("(?:^macro.*(?:\\{$|#.*)|^automacro.*(?:\\{$|#.*))")) {
                                break;
                            }

                            if (currentLine.contentEquals("[")) {
                                countOpenBlock++;
                            } else if (currentLine.contentEquals("]")) {
                                countOpenBlock--;
                            }
                        }

                        // Create closes scope, if need
                        if (countOpenBlock > 0) {
                            closesScope = "\n" + tab.substring(1) + "]";
                        }
                    }
                }

                // Replace old code with the new code formatted
                try {
                    macroCode.getDocument().insertString(macroCode.getCaretPosition(), tab + closesScope, null);

                    if (startBlock || startBlockBracket) {
                        macroCode.setCaretPosition(macroCode.getCaretPosition() - closesScope.length());
                    }
                } catch (BadLocationException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            
            // Update inform total rows
            labelSizeMacro.setText("Rows: " + Integer.toString(listRows.length));
        }

        // Update automacro/macro/variable list and auto-complete var
        // TODO: Future can be optimized!!
        Integer i = 0;
        DefaultListModel temporarilyListMacro = new DefaltListModel();
        DefaultListModel temporarilyListAutomacro = new DefaltListModel();
        DefaultListModel temporarilyListVariable = new DefaltListModel();

        for (String currentRow : listRows) {
            i++;
            Boolean find = false;
            currentRow = currentRow.replaceAll("^\\t+", "");

            Pattern p = Pattern.compile("^macro\\s*(.*)\\s*(?:\\{$|#.*)");
            Matcher m = p.matcher(currentRow);

            while (m.find()) {
                temporarilyListMacro.addElement(m.group(1) + "  -  " + Integer.toString(i));
                //autoCompleteItem = new JMenuItem(m.group(1) + "   -   macro");
                //autoCompleteItem.addInputMethodListener(autoComplete(m.group(1), macroCode));
                // corrigir para so ativar o evento quando for clicado
                //autoComplete.add(autoCompleteItem);
                
                find = true;
            }
            if (find == false) {
                p = Pattern.compile("^automacro\\s*(.*)\\s*(?:\\{$|#.*)");
                m = p.matcher(currentRow);

                while (m.find()) {
                    temporarilyListAutomacro.addElement(m.group(1) + "  -  " + Integer.toString(i));
                    find = true;
                }
                if (find == false) {
                    p = Pattern.compile("^var\\s*(\\w*)");
                    m = p.matcher(currentRow);

                    while (m.find()) {
                        temporarilyListVariable.addElement(m.group(1) + "  -  " + Integer.toString(i));
                        autoCompleteItem = new JMenuItem(m.group(1) + "   -   var");
                        autoCompleteItem.addInputMethodListener(autoComplete(m.group(1), macroCode));
                        // corrigir para so ativar o evento quando for clicado
                        autoComplete.add(autoCompleteItem);
                    }
                }
            }
        }

        listMacro.setModel(temporarilyListMacro);
        listAutomacro.setModel(temporarilyListAutomacro);
        listVariable.setModel(temporarilyListVariable);
    }//GEN-LAST:event_macroCodeKeyTyped

    private void macroCodeCaretUpdate(javax.swing.event.CaretEvent evt) {//GEN-FIRST:event_macroCodeCaretUpdate
        String[] listRows = macroCode.getText().split("\n");
        
        // Update inform caret position
        currentNumberRow = getRow(evt.getDot(), (JTextComponent) evt.getSource());
        currentColumnRow = getColumn(evt.getDot(), (JTextComponent) evt.getSource());
        labelCaretPosition.setText("Ln: " + Integer.toString(currentNumberRow)
                + "  Col:" + Integer.toString(currentColumnRow));
        
        // Uncheck previous marking the scope, if need
        // FIXME: If, while marked, insert the key, result in an error
        if (scopeMarked) {
            SimpleAttributeSet attrs = new SimpleAttributeSet();  
            StyleConstants.setForeground(attrs, Color.BLACK);
            
            StyledDocument styledCode = macroCode.getStyledDocument();
            
            if (positionOpenScope > 0) {
                styledCode.setCharacterAttributes(positionOpenScope, 1, attrs, false);
            }
            
            if (positionCloseScope > 0) {
                styledCode.setCharacterAttributes(positionCloseScope, 1, attrs, false);
            }
            
            scopeMarked = false;
        }
        
        // Mark the begin and end of the scope
        try {
            String nextCharacter = macroCode.getText(macroCode.getCaretPosition(), 1);
            if (nextCharacter.contentEquals("{") || nextCharacter.contentEquals("}") ||
                    nextCharacter.contentEquals("(") || nextCharacter.contentEquals(")") ||
                    nextCharacter.contentEquals("[") || nextCharacter.contentEquals("]")) {
                SimpleAttributeSet attrs = new SimpleAttributeSet();  
                StyleConstants.setForeground(attrs, Color.RED);
                scopeMarked = true;
                Integer countOpenBlock = 0;
                Integer countCharacter = macroCode.getCaretPosition();
                
                // Mark open
                StyledDocument styledCode = macroCode.getStyledDocument();
                styledCode.setCharacterAttributes(macroCode.getCaretPosition(), 1, attrs, false);
                positionOpenScope = macroCode.getCaretPosition();
                
                // Find close of...
                switch (nextCharacter) {
                    case "{":
                        {
                            boolean inEval = false;
                            Integer line = currentNumberRow;
                            
                            // Check if the keys are in some eval
                            String fristLine = listRows[line - 1].replaceAll("^\\t+", "");
                            if (fristLine.matches("^eval.*")) {
                                inEval = true;
                            } else {
                                Pattern p = Pattern.compile("(.*)eval\\((.*)\\)(.*)");
                                Matcher m = p.matcher(listRows[line - 1]);
                                while (m.find()) {
                                    if (!m.group(2).isEmpty() &&
                                            (m.group(1).length() + "eval(".length()) < currentColumnRow &&
                                            (m.group(1).length() + "eval(".length() + m.group(2).length() + ")".length() + m.group(3).length()) > currentColumnRow) {
                                        inEval = true;
                                    }
                                }
                            }
                            
                            if (inEval == false) {
                                // Add all the characters that can appear after the {, to add to the countCharacter
                                Pattern p = Pattern.compile("\\{(?:\\s*#.*|$)(.*)");
                                Matcher m = p.matcher(fristLine);
                                while (m.find()) {
                                    countCharacter += m.group(1).length();
                                }
                                String currentLine = "";
                                // Count how many characters there is until you get to the close escope
                                while (countOpenBlock >= 0 && listRows.length > line && currentLine.matches("(?:^macro.*\\{$|^automacro.*\\{$)") == false) {
                                    countCharacter++; // Line breaks should also be taken into consideration
                                    countCharacter += listRows[line].length();
                                    currentLine = listRows[line].replaceAll("^\\t+", "");

                                    if (currentLine.matches("^if.*\\{")) {
                                        countOpenBlock++;
                                    } else if (currentLine.matches("^}.*") && countOpenBlock == 0) {
                                        countOpenBlock--;
                                        // Ignore all the characters that can appear after the }, to reduce at the countCharecter
                                        p = Pattern.compile("^}(.*)");
                                        m = p.matcher(currentLine);

                                        while (m.find()) {
                                            countCharacter -= m.group(1).length();
                                        }
                                    } else if (currentLine.matches("^}") && countOpenBlock > 0) {
                                        countOpenBlock--;
                                    }

                                    line++;
                                }
                            } else {
                                // Count how many characters there is until you get to the open escope
                                // TODO: Por para evitar ir além de quantas caracteres tem o macroCode
                                while (countOpenBlock >= 0) {
                                    countCharacter++;

                                    if (macroCode.getText(countCharacter, 1).contentEquals("{")) {
                                        countOpenBlock++;
                                    } else if (macroCode.getText(countCharacter, 1).contentEquals("}")) {
                                        countOpenBlock--;
                                    }
                                }
                            }

                        
                            break;
                        }
                    case "}":
                        {
                            boolean inEval = false;
                            Integer line = currentNumberRow;
                            
                            // Check if the keys are in some eval
                            String fristLine = listRows[line - 1].replaceAll("^\\t+", "");
                            if (fristLine.matches("^eval.*")) {
                                inEval = true;
                            } else {
                                Pattern p = Pattern.compile("(.*)eval\\((.*)\\)(.*)");
                                Matcher m = p.matcher(listRows[line - 1]);
                                while (m.find()) {
                                    if (!m.group(2).isEmpty() &&
                                            (m.group(1).length() + "eval(".length()) < currentColumnRow &&
                                            (m.group(1).length() + "eval(".length() + m.group(2).length() + ")".length() + m.group(3).length()) > currentColumnRow) {
                                        inEval = true;
                                    }
                                }
                            }
                            if (inEval == false) {
                                line -= 2;
                                // Count how many characters there is until you get to the close escope
                                while (countOpenBlock >= 0 && 0 <= line) {
                                    countCharacter--; // Line breaks should also be taken into consideration
                                    countCharacter -= listRows[line].length();
                                    String currentLine = listRows[line].replaceAll("^\\t+", "");

                                    if (currentLine.matches("^}")) {
                                        countOpenBlock++;
                                    } else if (currentLine.matches("(?:^if.*\\{|^macro.*\\{|^automacro.*\\{|^call.*\\{)") && countOpenBlock == 0) {
                                        countOpenBlock--;

                                        // Count how many characters there are to the open scope
                                        Pattern p = Pattern.compile("(.*)\\{");
                                        Matcher m = p.matcher(currentLine);

                                        while (m.find()) {
                                            countCharacter += m.group(1).length();
                                        }
                                    } else if (currentLine.matches("^if.*\\{") && countOpenBlock > 0) {
                                        countOpenBlock--;
                                    }

                                    line--;
                                }
                            } else {
                                // Count how many characters there is until you get to the open escope
                                // TODO: Por para evitar ir além de quantas caracteres tem o macroCode
                                while (countOpenBlock >= 0) {
                                    countCharacter--;

                                    if (macroCode.getText(countCharacter, 1).contentEquals("}")) {
                                        countOpenBlock++;
                                    } else if (macroCode.getText(countCharacter, 1).contentEquals("{")) {
                                        countOpenBlock--;
                                    }
                                }
                            }

                            break;
                        }
                    case "[":
                        {
                            Integer line = currentNumberRow;
                            // Add all the characters that can appear after the [, to add to the countCharacter
                            String fristLine = listRows[line - 1].replaceAll("^\\t+", "");
                            Pattern p = Pattern.compile("^\\[(.*)");
                            Matcher m = p.matcher(fristLine);

                            while (m.find()) {
                                countCharacter += m.group(1).length();
                            }
                            String currentLine = "";
                            // Count how many characters there is until you get to the close escope
                            while (countOpenBlock >= 0 && listRows.length > line && currentLine.matches("(?:^macro.*\\{$|^automacro.*\\{$)") == false) {
                                countCharacter++; // Line breaks should also be taken into consideration
                                countCharacter += listRows[line].length();
                                currentLine = listRows[line].replaceAll("^\\t+", "");
     
                                if (currentLine.matches("^\\[.*")) {
                                    countOpenBlock++;
                                } else if (currentLine.matches("^].*") && countOpenBlock == 0) {
                                    countOpenBlock--;                     
                                    // Ignore all the characters that can appear after the ], to reduce at the countCharecter
                                    p = Pattern.compile("^](.*)");
                                    m = p.matcher(currentLine);

                                    while (m.find()) {
                                        countCharacter -= m.group(1).length();
                                    }
                                } else if (currentLine.matches("^]") && countOpenBlock > 0) {
                                    countOpenBlock--;
                                }
                                
                                line++;
                            }
                            
                            break;
                        }
                    case "]":
                        {
                            Integer line = currentNumberRow;
                            // Remove all the characters that can appear after the ], to remove to the countCharacter
                            String currentLine = "";
                            line -= 2;
                            // Count how many characters there is until you get to the open escope
                            while (countOpenBlock >= 0 && 0 < line && currentLine.matches("(?:^macro.*\\{$|^automacro.*\\{$)") == false) {
                                countCharacter--; // Line breaks should also be taken into consideration
                                countCharacter -= listRows[line].length();
                                currentLine = listRows[line].replaceAll("^\\t+", "");
               
                                if (currentLine.matches("^]")) {
                                    countOpenBlock++;
                                } else if (currentLine.matches("^\\[.*")) {
                                    countOpenBlock--;
                                }
                                
                                line--;
                            }
                            
                            break;
                        }
                    case "(":
                        // Count how many characters there is until you get to the close escope
                        // TODO: Por para evitar ir além de quantas caracteres tem o macroCode
                        while (countOpenBlock >= 0) {
                            countCharacter++;
                            
                            if (macroCode.getText(countCharacter, 1).contentEquals("(")) {
                                countOpenBlock++;
                            } else if (macroCode.getText(countCharacter, 1).contentEquals(")")) {
                                countOpenBlock--;
                            }
                        }
     
                        break;
                    case ")":
                        // Count how many characters there is until you get to the open escope
                        // TODO: Por para evitar ir além de quantas caracteres tem o macroCode
                        while (countOpenBlock >= 0) {
                            countCharacter--;
                            
                            if (macroCode.getText(countCharacter, 1).contentEquals(")")) {
                                countOpenBlock++;
                            } else if (macroCode.getText(countCharacter, 1).contentEquals("(")) {
                                countOpenBlock--;
                            }
                        }
                        break;
                }
                
                // Mark close
                if (countOpenBlock < 0) {
                    styledCode.setCharacterAttributes(countCharacter, 1, attrs, false);
                    positionCloseScope = countCharacter;
                }
            }
        } catch (BadLocationException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_macroCodeCaretUpdate

    private void itemRunOpenKoreActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemRunOpenKoreActionPerformed
        /*
         * Future can be keep in a file's address OpenKore,
         * to run here, accelerating tests
         */
    }//GEN-LAST:event_itemRunOpenKoreActionPerformed

    private void listMacroMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_listMacroMouseClicked
        Pattern p = Pattern.compile("\\w*\\s*-\\s*(\\d*)");
        Matcher m = p.matcher(listMacro.getSelectedValue().toString());
        Integer line = 0;

        while (m.find()) {
            line = Integer.parseInt(m.group(1));
        }
        
        listMacro.setFocusable(false);
        moveCaret(line, (JTextComponent) macroCode);
    }//GEN-LAST:event_listMacroMouseClicked

    private void listAutomacroMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_listAutomacroMouseClicked
        Pattern p = Pattern.compile("\\w*\\s*-\\s*(\\d*)");
        Matcher m = p.matcher(listAutomacro.getSelectedValue().toString());
        Integer line = 0;

        while (m.find()) {
            line = Integer.parseInt(m.group(1));
        }
        
        listAutomacro.setFocusable(false);
        moveCaret(line, (JTextComponent) macroCode);
    }//GEN-LAST:event_listAutomacroMouseClicked

    private void listVariableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_listVariableMouseClicked
        Pattern p = Pattern.compile("\\w*\\s*-\\s*(\\d*)");
        Matcher m = p.matcher(listVariable.getSelectedValue().toString());
        Integer line = 0;

        while (m.find()) {
            line = Integer.parseInt(m.group(1));
        }
        
        listVariable.setFocusable(false);
        moveCaret(line, (JTextComponent) macroCode);
    }//GEN-LAST:event_listVariableMouseClicked

    private void itemListDefinitionsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemListDefinitionsActionPerformed
        if (listTabbed.isVisible()) {
            listTabbed.setVisible(false);
        } else {
            listTabbed.setVisible(true);
        }
    }//GEN-LAST:event_itemListDefinitionsActionPerformed

    private void itemGoToLineActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemGoToLineActionPerformed
        String result = JOptionPane.showInputDialog(this,
                "Enter the line number you want to go", currentNumberRow);
        
        try {
            Integer.parseInt(result);
            moveCaret(Integer.parseInt(result), (JTextComponent) macroCode);
        } catch(NumberFormatException e) {
            JOptionPane.showMessageDialog(this,
                    "Enter only whole numbers!", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_itemGoToLineActionPerformed

    private void itemOpenPageInternationalPluginActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemOpenPageInternationalPluginActionPerformed
       try {
         String url = "http://openkore.com/index.php/Macro_plugin";
         java.awt.Desktop.getDesktop().browse(java.net.URI.create(url));
       } catch (java.io.IOException e) {
           System.out.println(e.getMessage());
       }
    }//GEN-LAST:event_itemOpenPageInternationalPluginActionPerformed

    private void itemOpenPageBrazilianPluginActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemOpenPageBrazilianPluginActionPerformed
       try {
         String url = "http://w.openkore-brasil.com/Macro_Manual";
         java.awt.Desktop.getDesktop().browse(java.net.URI.create(url));
       } catch (java.io.IOException e) {
           System.out.println(e.getMessage());
       }
    }//GEN-LAST:event_itemOpenPageBrazilianPluginActionPerformed

    private void itemOpenPageInternationalIDEActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemOpenPageInternationalIDEActionPerformed
        // TODO: Future will be created in the international wiki article about about this IDE
    }//GEN-LAST:event_itemOpenPageInternationalIDEActionPerformed

    private void itemOpenPageBrazilianIDEActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_itemOpenPageBrazilianIDEActionPerformed
        // TODO: Future will be created in the brazilian wiki article about about this IDE
    }//GEN-LAST:event_itemOpenPageBrazilianIDEActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Main().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPopupMenu autoComplete;
    private javax.swing.JMenuItem itemFormat;
    private javax.swing.JMenuItem itemGoToLine;
    private javax.swing.JMenuItem itemListDefinitions;
    private javax.swing.JMenuItem itemOpenMacro;
    private javax.swing.JMenuItem itemOpenPageBrazilianIDE;
    private javax.swing.JMenuItem itemOpenPageBrazilianPlugin;
    private javax.swing.JMenuItem itemOpenPageInternationalIDE;
    private javax.swing.JMenuItem itemOpenPageInternationalPlugin;
    private javax.swing.JMenuItem itemRunOpenKore;
    private javax.swing.JMenuItem itemSaveMacro;
    private javax.swing.JMenuItem itemSaveMacroAs;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenu jMenu2;
    private javax.swing.JMenu jMenu3;
    private javax.swing.JMenu jMenu4;
    private javax.swing.JMenu jMenu5;
    private javax.swing.JMenu jMenu6;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JToolBar.Separator jSeparator1;
    private javax.swing.JToolBar.Separator jSeparator2;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JLabel labelAddressFile;
    private javax.swing.JLabel labelCaretPosition;
    private javax.swing.JLabel labelSizeMacro;
    private javax.swing.JList listAutomacro;
    private javax.swing.JList listMacro;
    private javax.swing.JTabbedPane listTabbed;
    private javax.swing.JList listVariable;
    private javax.swing.JTextPane macroCode;
    // End of variables declaration//GEN-END:variables
}
